package com.pentajock.docdb.dialogs;

import com.pentajock.docdb.Main;
import com.pentajock.docdb.colors.Colors;
import com.pentajock.docdb.database.DBController;
import com.pentajock.docdb.gui.ListRenderer;
import com.pentajock.docdb.images.DBImages;
import com.pentajock.docdb.lang.Lang;
import com.pentajock.docdb.types.Document;
import com.pentajock.docdb.types.User;
import com.pentajock.docdb.utils.Utils;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.border.*;
import javax.swing.UIManager.*;

public class Dialogs {
	private static final int INFO_MSG = JOptionPane.INFORMATION_MESSAGE;
	private static final int WARN_MSG = JOptionPane.WARNING_MESSAGE;
	private static final int ERR_MSG = JOptionPane.ERROR_MESSAGE;
	private static final int QUES_MSG = JOptionPane.QUESTION_MESSAGE;
	private static final int PLN_MSG = JOptionPane.PLAIN_MESSAGE;
	private static final int YES = JOptionPane.YES_OPTION;
	private static final int YES_NO = JOptionPane.YES_NO_OPTION;
	private static final int YES_NO_CANCEL = JOptionPane.YES_NO_CANCEL_OPTION;
	private static final int OK_CANCEL = JOptionPane.OK_CANCEL_OPTION;
	private static final int OK = JOptionPane.OK_OPTION;
	private static final int DEFAULT = JOptionPane.DEFAULT_OPTION;
	
	private static final String COLON = Lang.MISC.getText("CHOICE_DELIMITER", ":");
	private static final String ERROR_TITLE = Lang.DIALOG.getText("ERROR_TITLE", "Error");
	
	/*
	 * Default constructor
	 */
	public Dialogs() {
		// Does nothing
	}
	
	/* 
	 * Data type used to store fancy dialog components
	 * Reduces redundant dialog code by using getDialogComponent,
	 * which returns one of these data types
	 */
	private static class DialogComponent {
		public final String name;
		public final String type;
		public final Icon icon;
		public DialogComponent(String name, String type, Icon icon) {
			this.name = name;
			this.type = type;
			this.icon = icon;
		}
	}
	
	/*
	 * Shows a generic list dialog with the specified list of components
	 * Returns the selected component as an object
	 */
	public static Object showListDialog(Component parent, java.util.List<?> o) {
		JDialog dialog;
		final JList list = new JList(o.toArray());
		Object[] msg = {Lang.DIALOG.getText("LIST_SELECT", "Select an item") + COLON, new JScrollPane(list)};
		final JOptionPane op = new JOptionPane(msg, PLN_MSG, OK_CANCEL, null);
		dialog = op.createDialog(parent, (String)msg[0]);
		list.setSelectedIndex(0);
		list.setCellRenderer(new ListRenderer());
		list.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if(e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
					op.setValue(OK);
					SwingUtilities.getWindowAncestor(e.getComponent()).setVisible(false);
				}
			}
		});
		
		dialog.addWindowListener(new WindowAdapter() {
			public void windowOpened(WindowEvent e) {
				list.requestFocusInWindow();
			}
			
			public void windowActivated(WindowEvent e) {
				list.requestFocusInWindow();
			}
		});
		
		//Utils.setIconImage(dialog, DBImages.CLIENT_WINDOW.getImage());
		
		dialog.setVisible(true);
		
		if (((Integer)op.getValue()).equals(OK))
			return list.getSelectedValue();
		return null;
	}
	
	/*
	 * Shows a pre-made error dialog, with a trace to the calling class
	 */
	public static boolean showError() {
		return showError(null);
	}
	
	/* 
	 * Shows a generic error dialog with the specified message
	 */
	public static boolean showError(Object o) {
		String message;
		if (o instanceof String) {
			message = (String)o;
		}
		else if (o instanceof Exception) {
			return showError((Exception)o, ((Exception)o).getMessage());  
		}
		else
			message = null;
		
		if (message == null) {
			try {
				throw new Exception(); 
			}
			catch (Exception e) {
				message = "Error performing this action (" + e.getStackTrace()[2].getClassName() + ")";
			}
		}
		Main.printDebug(message);
		JOptionPane.showMessageDialog(null, message, ERROR_TITLE, ERR_MSG);
		return false;
	}
	
	public static boolean showError(Object o, String message) {
		return showMessage(ERR_MSG, new Object[] {o}, new String[] {message});
	}
	
	public static boolean showError(Exception e, Object o, String message) {
		return showMessage(ERR_MSG, new Object[] {o, null}, new Object[] {message, getBugButton(e)});
	}
	
	/*
	 * Shows an error message dialog with a stack trace bug report button
	 * and prints the error to the debug log
	 */
	public static boolean showError(Exception e, String message) {
		Main.printStackTrace(e, message);
		JPanel errorPanel = new JPanel(new FlowLayout());
		errorPanel.add(new JLabel(message));
		errorPanel.add(getBugButton(e));
		JOptionPane.showMessageDialog(null, errorPanel, ERROR_TITLE, ERR_MSG);
		return false;
	}

	/* 
	 * Creates a "Bug Button", which is actually inside 
	 * a JPanel for use in the error dialogs. The button
	 * opens a new dialog displaying the exception stack
	 * trace.
	 */
	private static JComponent getBugButton(final Exception e) {
		JPanel bugPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		JButton bugButton = new JButton(new AbstractAction(null, DBImages.BUG_ICON.getIcon()) {
				public void actionPerformed(ActionEvent a) {
					StringWriter sw = new StringWriter();
					e.printStackTrace(new PrintWriter(sw));
					JTextArea bugArea = new JTextArea(sw.toString());
					JScrollPane bugScroll = new JScrollPane(new JTextArea(sw.toString()));
					bugScroll.setPreferredSize(new Dimension(500,200));
					JOptionPane.showMessageDialog(null, bugScroll, "Stack Trace:", ERR_MSG);
				}
			});
		bugButton.setBorder(BorderFactory.createEtchedBorder());
		bugButton.setToolTipText(Lang.TOOL_TIP.get("STACK_TRACE", "Stack trace"));
		bugPanel.add(bugButton);
		return bugPanel;
	}
	
	/*
	 * Gets some values such as Name, Type, and Icon 
	 * that the advanced dialogs use to display
	 */
	private static DialogComponent getDialogComponent(Object o) {
		if (o instanceof User)
			return new DialogComponent(((User)o).getUserName(), 
				Lang.MISC.getText("USER", "user"), DBImages.getUserIcon(o));
		else if (o instanceof Document) {
			Document d = (Document)o;
			try {
				return new DialogComponent(d.getFullName(), 
					Lang.MISC.getText("DOCUMENT", "document"), DBImages.getMimeType(o));
			}
			catch (Exception e) {
				return new DialogComponent(d.getFullName(), 
					Lang.MISC.getText("DOCUMENT", "document"),DBImages.GENERIC_ICON.getIcon());
			}
		}
		else if (o instanceof File) {
			if (Utils.getExtension(o).equalsIgnoreCase("ini"))	// Catch for language file
				return new DialogComponent(Lang.MISC.getText("THIS_LANGUAGE", Utils.getSubject(o)),
					Lang.MISC.getText("LANGUAGE", "language"), DBImages.getLanguageIcon(o));
			try {
				return new DialogComponent(((File)o).getName(), 
					Lang.MISC.getText("DOCUMENT", "document"), DBImages.getMimeType(o));
			}
			catch (Exception e) {
				return new DialogComponent(((File)o).getName(), 
					Lang.MISC.getText("DOCUMENT", "document"), DBImages.GENERIC_ICON.getIcon());
			}
		}
		else if (o instanceof String)
			return new DialogComponent(((String)o), 
				Lang.MISC.getText("ITEM", "item"), DBImages.GENERIC_ICON.getIcon());
		else if (o instanceof LookAndFeelInfo)
			return new DialogComponent(((LookAndFeelInfo)o).getName(), 
				Lang.MISC.getText("THEME", "theme"), DBImages.getLookAndFeelIcon(o));
		else if (o instanceof URI)
			return new DialogComponent("<A HREF=\"" + (URI)o + "\">" + (new File((URI)o)).getName() + "</A>",
				Lang.MISC.getText("LINK", "link"), null);
		else if (o instanceof Colors)
			return new DialogComponent(((Colors)o).getName(),
				Lang.MISC.getText("COLOR", "color"), DBImages.COLOR_ICON.getIcon());
		else if (o == null)
			return new DialogComponent("", "", null);
		else
			return new DialogComponent("Unknown " + o.toString(), 
				Lang.MISC.getText("ITEM", "item"), DBImages.GENERIC_ICON.getIcon());
	}
	
		
	/*
	 * Shows a generic success dialog
	 */
	public static boolean showSuccess(String message) {
		JOptionPane.showMessageDialog(null, message, Lang.DIALOG.getText("SUCCESS_TITLE", "Succeeded"), WARN_MSG);
		return true;
	}
	
	/*
	 * Shows a fancy success dialog
	 */
	public static boolean showSuccess(Object[] components, String[] message) {
		/*Object[] msg = new Object[2*components.length];
		for (int i=0; i < msg.length;i+=2) {
			msg[i] = message[i/2];
			msg[i+1] = getDialogLabel(components[i/2]);
		}
		new JOptionPane().showMessageDialog(null, msg, Lang.DIALOG.getText("SUCCESS_TITLE", "Succeeded"), JOptionPane.INFORMATION_MESSAGE);*/
		return showMessage(INFO_MSG, components, message);
	}
	
	/*
	 * Shows most of the fancy message dialogs.
	 * Called on by showError, showSuccess, etc
	 */
	private static boolean showMessage(int msgType, Object[] components, Object[] message) {
		return showMessage(null, msgType, components, message);
	}
	
	private static boolean showMessage(JComponent parent, int msgType, Object[] components, Object[] message) {
		Object[] msg = new Object[2*components.length];
		for (int i=0; i < msg.length;i+=2) {
			msg[i] = message[i/2];
			msg[i+1] = getDialogLabel(components[i/2]);
		}
		String title;
		switch (msgType) {
			case INFO_MSG:
				title = Lang.DIALOG.getText("SUCCESS_TITLE", "Succeeded"); break;
			case WARN_MSG:
				title = Lang.DIALOG.getText("WARNINTG_TITLE", "Warning"); break;
			case ERR_MSG:
				title = Lang.DIALOG.getText("ERROR_TITLE", "Error"); break;
			default:title = "Message";
		}
		
		new JOptionPane().showMessageDialog(parent, msg, title, msgType);
		return msgType==ERR_MSG?false:true;
	}
	
	/* 
	 * Shows a fancy success dialog
	 * Convenience method for showSuccess(Object[], String[])
	 */
	public static boolean showSuccess(Object o, String s) {
		return showSuccess(new Object[]{o}, new String[]{s});
	}
	
	/* 
	 * Shows a fancy success dialog
	 * Convenience method for showSuccess(Object[], String[])
	 */
	public static boolean showSuccess(Object o, String s, Object p, String t) {
		return showSuccess(new Object[]{o, p}, new String[]{s, t});
	}
	
		
	/* 
	 * Shows a generic confirmation dialog
	 */
	public static Boolean showConfirm(String msg) {
		return JOptionPane.showConfirmDialog(null, msg, 
			Lang.DIALOG.getText("CONFIRM_TITLE", "Confirmation Needed"), 
			YES_NO) == YES; 
	}
	
	public static int showYesNoCancel(String msg) {
		return JOptionPane.showConfirmDialog(null, msg, 
			Lang.DIALOG.getText("CONFIRM_TITLE","Confirmation Needed"),
			YES_NO_CANCEL);
	}
	
	/*
	 * Shows a fancy confirmation dialog
	 */
	public static Boolean showConfirm(Object o, String msg) {
		return JOptionPane.showConfirmDialog(null, new Object[] {msg, getDialogLabel(o)}, 
			Lang.DIALOG.getText("CONFIRM_TITLE", "Confirmation Needed"), 
			YES_NO) == YES;
	}
	
	/*
	 * Shows a fancy confirm with alert icon
	 */
	 public static Boolean showAlertConfirm(Object o, String msg) {
		 return JOptionPane.showConfirmDialog(null, new Object[] {msg, getDialogLabel(o)}, msg, YES_NO, WARN_MSG) == YES;
	 }
	
	/*
	 * Confimation dialog for deleting something
	 * Changes dynamically depending on what you're deleting!
	 */
	public static boolean confirmDelete(Object o) {
		String message = Lang.DIALOG.getText("CONFIRM_DELETE", "Confirm Delete") + " (<B>" + getDialogComponent(o).type + "</B>)" + COLON;
		Object[] msg = {"<HTML>" + message + "</HTML>", getDialogLabel(o)};
		JOptionPane op = new JOptionPane(msg, QUES_MSG, OK_CANCEL, null);
		op.createDialog(null, Lang.DIALOG.getText("CONFIRM_DELETE", "Confirm Delete")).setVisible(true);
		Object s = op.getValue();
		if(((Integer)s).equals(OK))
			return true;
		return false;
	}
	
	/*
	 * Returns a JLabel for use in a fancy dialog
	 */
	static private JLabel getDialogLabel(Object o) {
		DialogComponent c = getDialogComponent(o);
		JLabel j = new JLabel("<HTML><B>" + c.name + "</B></HTML>", c.icon, SwingConstants.LEFT);
		j.setBorder(new EmptyBorder(0, 20, 0, 0));
		return j;
	}
	
	/*
	 * General about-dialog for
	 * application
	 */
	static public void showAboutDialog() {
		final String gpl = "http://www.gnu.org/copyleft/gpl.html";
		String title = Lang.MISC.getText("TITLE", "Document Database");
		JLabel liscLabel = new JLabel("<HTML><B>" + Lang.MISC.getText("LISCENSE", "Liscense") + COLON + "</B><UL><LI>" +
				"<A HREF=\"" + gpl + "\">GNU/GPL" + 
				" (http://www.gnu.org/copyleft/gpl.html)</A></UL></HTML>");
		liscLabel.addMouseListener(new MouseAdapter() {
                        public void mouseClicked(MouseEvent me) {
					if (me.getButton() == MouseEvent.BUTTON1)
					try {
						Utils.desktopCall("browse", new URI(gpl));
					}
					catch (Exception e) {
						showError(e, Lang.DIALOG.get("BROWSER_ERROR", "Can't launch browser"));
					}
			}
		});
		Main.setCursor(liscLabel, Cursor.HAND_CURSOR);
		
		JComponent[] jC = new JComponent[] {
			new JLabel("<HTML><B>" + title + "</B> " + Main.VERSION_NUMBER + "</HTML>",
				DBImages.CLIENT_WINDOW.getIcon(), SwingConstants.CENTER),
			new JSeparator(), liscLabel,
			new JLabel("<HTML><B>" + Lang.MISC.getText("AUTHOR", "Author") + COLON + 
				Lang.MISC.get("AUTHOR_LIST", "Tres Finocchiaro, Steve Kurilovitch") + 
				"</B></HTML>"),
		};
		JPanel jP = new JPanel(new GridLayout(jC.length, 1, 0, 0));
		for (JComponent j:jC)
			jP.add(j);
		
		/* Creates dialog from the JOptionPane, and sets it visible */
		new JOptionPane(jP, PLN_MSG, DEFAULT, null).createDialog(
			null, Lang.MENU.getText("ABOUT", "About") + COLON + " " + title).setVisible(true);
	}

	/*
	 * Prompts the user for a category name
	 * based on the different categories available
	 * in the database.  Recurses if null.
	 */
	static public String categoryNameDialog(Object o) {
		Object[] msg  = new Object[]{Lang.DIALOG.get("CHOOSE_CATEGORY", "Please choose a category") + COLON, getDialogLabel(o)};
		java.util.List<String> categoryList = DBController.getEnabledCategories();
		Object defaultValue;
		String langProcedure = Lang.MISC.getText("DEFAULT_CATEGORY", "Procedure");
		if (categoryList.contains(langProcedure)) defaultValue = langProcedure;
		else defaultValue = categoryList.get(0);
		Object results = JOptionPane.showInputDialog(null, msg, Lang.DIALOG.getText("LIST_SELECT", "Select an item"), 
			QUES_MSG, null, categoryList.toArray(), defaultValue);
		if (results == null)
			return categoryNameDialog(o);
		return results.toString();
	}
	
	/*
	 * Dialog component for a category (a super-hack of JOptionPane)
	 * respect the hackage :)
	 */
	 static public void categoryDialog(String category) {
		JComponent[] jC = new JComponent[8];
		 
		jC[0] = new JLabel(Lang.MISC.get("CATEGORY", "Category") + COLON, DBImages.CATEGORY_ICON.getIcon(), SwingConstants.RIGHT);
		jC[2] = new JLabel(Lang.MISC.get("DEFAULT_INTERVAL", "Default Interval") + COLON, DBImages.CLOCK_ICON.getIcon(), SwingConstants.RIGHT);
		jC[4] = new JLabel(Lang.MISC.get("ENABLED", "Enabled") + COLON, 	DBImages.WAND_ICON.getIcon(), SwingConstants.RIGHT); 
		jC[6] = new JLabel(Lang.MISC.get("EXPIRABLE", "Expirable") + COLON, DBImages.CLOCK_ICON.getIcon(), SwingConstants.RIGHT); 
		
		jC[1] = new JTextField(category);
		jC[3] = new JTextField("" + DBController.getCategoryDefaultInterval(category));
		jC[5] = new JCheckBox("", DBController.getCategoryIsEnabled(category));
		jC[7] = new JCheckBox("", DBController.getCategoryIsExpirable(category));
		
		JPanel catPanel = new JPanel(new GridLayout(4, 2, 4, 4));
		for (JComponent j : jC)
			catPanel.add(j);
		 
		JOptionPane op = new JOptionPane(catPanel, PLN_MSG, OK_CANCEL, null);
		JDialog dialog = op.createDialog(null, Lang.MISC.get("CATEGORY", "Category") + COLON);
		Utils.setIconImage(dialog, DBImages.CATEGORY_ICON.getImage());
		dialog.setVisible(true);
		if (((Integer)op.getValue()).equals(OK)) {
			DBController.updateCategory(new Object[] {
				(String)((JTextField)jC[1]).getText(),
				(String)((JTextField)jC[3]).getText(),
				(Boolean)((JCheckBox)jC[5]).isSelected(),
				(Boolean)((JCheckBox)jC[7]).isSelected()
			});
		}
	 }
	 
	
	/* 
	 * Fancy input dialog that's mainly used for renaming
	 */
	static public String showInputDialog(Object o, String s) {
		Object results = JOptionPane.showInputDialog(null, 
			new Object[]{s, getDialogLabel(o)}, Utils.getSubject(getDialogComponent(o).name));
		return results==null?null:results.toString();
	}
	
	/*
	 * Dialog for getting the update interval.  
	 * Recurses if null, non-integer, or less than -2
	 */
	static public int updateIntervalDialog(String category, Object o) {
		Object[] msg  = new Object[]{Lang.DIALOG.get("CHOOSE_INTERVAL", "Please type interval") + COLON, getDialogLabel(o)};
		Object results = JOptionPane.showInputDialog(null, msg, "" + DBController.getDefaultInterval(category));
		try {
			if (Utils.isInt(results) && Utils.goodUpdateInterval(results))
			return Utils.getInt(results);
		}
		catch (Exception e) {
		}
		return updateIntervalDialog(category, o);
	}
}
	
